{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]]) #TODO\n",
    "\n",
    "y = np.array([0, 0, 1, 1]) #TODO  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "knn = KNeighborsClassifier(n_neighbors=3) #TODO"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: black;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  max-height: 0;\n",
       "  max-width: 0;\n",
       "  overflow: hidden;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  max-height: 200px;\n",
       "  max-width: 100%;\n",
       "  overflow: auto;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 1ex;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=3)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;KNeighborsClassifier<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.5/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>KNeighborsClassifier(n_neighbors=3)</pre></div> </div></div></div></div>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=3)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X, y) # TODO"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user) #TODO"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "index 1 is out of bounds for axis 0 with size 1",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[9], line 20\u001b[0m\n\u001b[0;32m     16\u001b[0m nearest \u001b[38;5;241m=\u001b[39m X[idx] \u001b[38;5;66;03m#TODO # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\u001b[39;00m\n\u001b[0;32m     18\u001b[0m \u001b[38;5;66;03m# 用红线标记新数据点与最近邻点的连接线\u001b[39;00m\n\u001b[0;32m     19\u001b[0m \u001b[38;5;66;03m# 提示：用plt.plot()绘制，用 r-- 实现红色虚线\u001b[39;00m\n\u001b[1;32m---> 20\u001b[0m plt\u001b[38;5;241m.\u001b[39mplot([new_user[\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m0\u001b[39m], nearest[\u001b[38;5;241m0\u001b[39m]], [new_user[\u001b[38;5;241m0\u001b[39m, \u001b[38;5;241m1\u001b[39m], nearest[\u001b[38;5;241m1\u001b[39m]], \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mr--\u001b[39m\u001b[38;5;124m'\u001b[39m) \u001b[38;5;66;03m#TODO\u001b[39;00m\n\u001b[0;32m     22\u001b[0m \u001b[38;5;66;03m# 为每个点添加坐标文本  \u001b[39;00m\n\u001b[0;32m     23\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m x, y \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(X[:, \u001b[38;5;241m0\u001b[39m], X[:, \u001b[38;5;241m1\u001b[39m]):\n",
      "\u001b[1;31mIndexError\u001b[0m: index 1 is out of bounds for axis 0 with size 1"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAioAAAHJCAYAAACrCBICAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAABAvUlEQVR4nO3deXgUVfr28btIOp2ELKwZCISERXYQEEYRFR0MCBGCCLIEDYsOKCqCqCOvC6iIOKOgo4IgRhwJYZRF1EFERkAcGTYXVARkXw17CIGkk5z3Dyb9IySEbujQBfl+risX9qnTp55+aOSmuqraMsYYAQAA2FA5fxcAAABwLgQVAABgWwQVAABgWwQVAABgWwQVAABgWwQVAABgWwQVAABgWwQVAABgWwQVAABgWwQVXFGWLl0qy7IUFxdX4rybb75ZlmXpvffeuyR1+UJcXJwsy5JlWZo1a9Y5561atco9z7KsS1jh/ymotTQNGDCg0Ou0LEshISFq2LChRowYof3795fq/gusXbtWHTt2VIUKFdx1bN++/ZLsGygLAv1dAADvzZw5U3379i122wcffHCJq/Gvdu3aqV69epKk9PR0rVy5UpMmTVJaWpq+/fbb84bWi3H8+HF169ZN+/bt080336yYmBhZlqWwsLBS2ydQ1hBUgMtMy5YttWjRIh04cEBVq1YttC03N1ezZ89W48aNtWXLFmVnZ/ulxiVLlsjlcl2Sfd17770aMGCA+/GBAwfUpUsXrVmzRqNGjdJHH31UavtevXq19u7dq7vvvlvvv/9+qe0HKMv46Ae4zPTv31+5ubn65z//WWTbF198ofT0dPXv398Plf2funXrqmHDhn7Zd9WqVfXKK69Ikj777LNSDUy7d++WJNWpU6fU9gGUdQQV4AyHDh3S6NGj1aRJE4WFhSkyMlL169fXPffco1WrVhWZf+DAAY0aNUoNGjRQcHCwKlasqM6dO2v58uVF5hacPzNgwADt379f9957r2rWrKnAwEBNmjTJ4xq7d++usLCwYj/i+eCDD2RZlvr161fiGt9++60SExNVtWpVOZ1OxcXF6YEHHtDevXsLzXvllVdkWZb+8pe/nHOtLl26yLIsLV682D1W0jkq27dv15AhQxQXFyen06mqVauqZ8+e+vHHH0us2RstW7aUJJ06dUoHDx6U9H/ntCxdulSLFi3S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+eUX3XfffcrKytLgwYPd82rXrq3rr79egwcPVm5urhITE8+5z6SkJFWpUkWJiYn6+uuvtW3bNi1btkzDhw/X7t27i33O9OnT1bNnTzVt2rTQz6BBg3T06FF99tlnXr3uevXq6R//+Ic2bNig//73v0pKSlJISEihOXFxcVqyZIn279+vI0eOeLV+gYYNG+rWW2/Vn//8Z61atUrfffed/vznPxd7lAeAdwgqQBkRERGhiIiIc25/6aWXdOedd+ruu+9Wq1at9Ntvv2nRokVFzrNISkrSDz/8oB49ehT5S/9MoaGhWr58uWrVqqUePXqoUaNGGjRokE6ePFlsHWvXrtUPP/ygO++8s8i28PBwdezY0et7qrz77rs6cuSIWrZsqbvvvtt9+fWZXnnlFS1evLjYS7a98f777+sPf/iDbrrpJt1xxx267777FB4eruDg4AteE4BkGT5EBQCf2717t2JiYvTll1+qQ4cO/i4HuGwRVADAB/79738rMzNTzZo10759+/T4449rz5492rRpU5ETcQF4jqt+AMAHXC6XRo8era1btyo8PFzXX3+9Zs6cSUgBLhJHVAAAgG1xMi0AALAtggoAALAtggoAALAtggoAALAtggoAALAtggoAALAtggoAALAtggoAALAtggoAALCt/w8o+DrVbRpd1wAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title(\"User Movie Preference\", size=15) \n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "# 绘制原始样本点，要求不同的影片喜好类别用不同的颜色标记\n",
    "# 提示: 用scatter散点图绘制，用它的参数c实现不同的类别用不同的颜色标记\n",
    "plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis') #TODO\n",
    "\n",
    "# 绘制新数据点，用红色x标记，大小为8\n",
    "# 提示：用plt.plot()绘制，用它的参数marker实现不同的标记符号\n",
    "plt.plot(new_user[0,0], new_user[0,1], 'rx', markersize=8)#TODO\n",
    "\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user) #TODO\n",
    "nearest = X[idx] #TODO # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "# 提示：用plt.plot()绘制，用 r-- 实现红色虚线\n",
    "plt.plot([new_user[0, 0], nearest[0]], [new_user[0, 1], nearest[1]], 'r--') #TODO\n",
    "\n",
    "# 为每个点添加坐标文本  \n",
    "for x, y in zip(X[:, 0], X[:, 1]):\n",
    "    plt.text(x, y+0.1, f'({x}, {y})') \n",
    "\n",
    "# 为新数据点添加坐标文本\n",
    "plt.text(new_user[0,0], new_user[0,1]+0.1, f'({new_user[0,0]}, {new_user[0,1]})')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
